/*
 * BitManager.cpp
 *
 *  Created on: 27/11/2009
 *      Author: ngonzalez
 */

#include "BitManager.h"

BitManager::BitManager() {

	this->stateControl = new StateControl();
	this->contadorByte=0;
	this->contadorcadena=0;
	this->finCompresion=false;
	this->finDescompresion = false;

}

BitManager::~BitManager() {
	delete this->stateControl;
}

void BitManager::normalize(){

	this->stateControl->setPiso(this->floor);
	this->stateControl->setTecho(this->roof);
	this->stateControl->asignar();
	this->stateControl->controlOverflow();
	this->updateAfterOV();
	this->stateControl->controlUnderflow();
	this->updateAfterUF();

}

string BitManager::getCompressedString(){
	if (!finCompresion) {
		this->stateControl->agregarPisoFinal();
		this->stateControl->agregarPading();
		finCompresion = true;
	}
	return this->stateControl->getEmision();
}

void BitManager::setStringToDecompress(string compressedString) {
	this->compressedString=compressedString;
}


unsigned int BitManager::getNormalizedFloor(){
	return floor;
}

unsigned int BitManager::getNormalizedRoof(){
	return roof;
}

void BitManager::updateAfterOV () {

	unsigned int i;
	unsigned int contadorOV= this->stateControl->getContadorOV();
    
    if (contadorOV == 0)
      return; //TODO Emprolijar esto
	
	unsigned int piso=this->stateControl->getPiso();
	unsigned int techo= this->stateControl->getTecho();
	

	for (i=0; i<contadorOV; i++) {
		piso = (piso<<1) +0 ;
		techo = (techo<<1) +1;

		//this->stateControl->mostrarBinario(piso,"pisoshift");
		//this->stateControl->mostrarBinario(techo,"techo shift");
	}

	this->floor=piso;
	this->roof=techo;

	this->stateControl->setPiso(floor);
	this->stateControl->setTecho(roof);
	this->stateControl->asignar();

}

void BitManager::updateAfterUF() {
	unsigned int i;
	unsigned int piso, bit0Piso;
	piso= this->stateControl->getPiso();
	unsigned int techo, bit0Techo;
	techo = this->stateControl->getTecho();
	unsigned int contadorUF;
	contadorUF= this->stateControl->getContadorUF();

	unsigned int bit0;
	//this->stateControl->mostrarBinario(piso,"pisoshiftantes");
	//this->stateControl->mostrarBinario(techo,"techo shift antes");

	bit0 = ~0;
	bit0 = (bit0>>1) +1;

	if (contadorUF>0) {

		bit0Piso= piso & bit0;
		bit0Techo= techo & bit0;
		//piso= piso<<1;
		//techo = techo<<1;

		for (i=0;i<contadorUF+1;i++) {
			piso = (piso<<1) + 0;
			techo = (techo<<1) + 1;
			//this->stateControl->mostrarBinario(piso,"pisoshift");
			//this->stateControl->mostrarBinario(techo,"techo shift");
		}

		piso= piso>>1;
		techo = techo>>1;
		//this->stateControl->mostrarBinario(piso,"pisoshift");
		//this->stateControl->mostrarBinario(techo,"techo shift");
		piso = piso | bit0Piso;
		techo = techo | bit0Techo;

		this->floor = piso;
		this->roof = techo;

	}

}

unsigned int BitManager::getTamanioCadena() {
	return this->tamanioCadena;
}

unsigned int BitManager::getContadorCadena() {
	return this->contadorcadena;
}


unsigned int BitManager::descomprimirUltimoCaracter(unsigned int entero) {
	//bool nopaso = true;
	unsigned char mascarab1;
	//unsigned int entero;
	unsigned char firstBit;
	mascarab1 = ~0;
	mascarab1 = (mascarab1>>1) +1;

	//entero = this->integer;

	/*if((tamanioCadena<5) && (noPaso)) {
		this->calcularTamanioPading();
		byte = compressedString[tamanioCadena-1];

	}*/
	//cout<<"ultimo caracter a descomprimir "<< static_cast<int>(byte)<<endl;
	//byte = this->compressedString[contadorcadena-1];
	if (contadorByte<8) {
		contadorByte++;
		firstBit= byte & mascarab1;
		byte=byte<<1;
		if(firstBit!=0) {
			entero = (entero<<1) +1;
		}
		else {
			entero= (entero<<1) +0;
		}
	}

	else{
		contadorByte = 0;
		contadorcadena++;
	}




	return entero;

}


unsigned int BitManager::asignarBit(unsigned int entero) {
	unsigned char mascarab1;
	unsigned char firstBit;
	mascarab1 = ~0;
	mascarab1 = (mascarab1>>1) +1;

	if (contadorByte>7) {
		contadorcadena++;
		if (contadorcadena<tamanioCadena-1) {
			byte=this->compressedString[contadorcadena];
			contadorByte=0;
		}
		else {
			byte = this->compressedString[contadorcadena];
			this->calcularTamanioPading();
			contadorByte = tamanioPading;
			entero=this->descomprimirUltimoCaracter(entero);
			return entero;
		}


	}

	if(contadorcadena<tamanioCadena-1) {
		contadorByte++;
		firstBit= byte & mascarab1;
		byte=byte<<1;

		if(firstBit!=0) {
			entero = (entero<<1) +1;

		}else {
				entero= entero<<1;

			}

	}


	return entero;
}

void BitManager::calcularEnteroOV() {

	unsigned int i, entero;
	entero = this->integer;
	unsigned int contadorOV=this->stateControl->getContadorOV();
	for(i=0;i<contadorOV;i++) {

		if(contadorcadena<tamanioCadena-1) {
			entero = this->asignarBit(entero);
		}
		else if(contadorcadena<tamanioCadena) {
			entero =this->descomprimirUltimoCaracter(entero);
		}
		else return;
	}

	this->integer= entero;
}

void BitManager::calcularEnteroUF() {
	unsigned int mascarab1, firstBit;
	mascarab1= ~0;
	mascarab1 = (mascarab1 >> 1) + 1;
	unsigned int entero = this->integer;
	unsigned int contadorUF = this->stateControl->getContadorUF();
	unsigned int i;

	firstBit = entero & mascarab1;

	for (i=0;i<contadorUF;i++) {


		if (contadorcadena < tamanioCadena-1) {
			entero = this->asignarBit(entero);
		}

		else if(contadorcadena < tamanioCadena)
			entero =this->descomprimirUltimoCaracter(entero);
		else return;
	}

	entero = entero<<1;
	entero = (entero>>1) | firstBit;

	this->integer = entero;




}

void BitManager::calcularTamanioPading() {
	unsigned char ultimoByte;
	unsigned char mascaraLastBit, lastBit;
	unsigned int i;

	mascaraLastBit =1;
	ultimoByte = this->compressedString[this->tamanioCadena-1];

	i=0;
	do {
		lastBit =mascaraLastBit & ultimoByte;
		ultimoByte = ultimoByte >>1;
		this->tamanioPading++;
		i++;
	} while ((lastBit !=1) && (i<8));







}

bool BitManager::isFinDescompresion() {
	return this->finDescompresion;
}

bool BitManager::interpretCompresedString(){
	unsigned int i, entero;

	this->tamanioCadena = this->compressedString.size();

	if (contadorcadena<4) {
		for (i=0;i<4;i++) {
			byte = this->compressedString[i];
			entero= (entero<<8) | byte;
			contadorcadena++;
			byte= this->compressedString[contadorcadena];
		}
		this->integer=entero;
	}
	else {
		calcularEnteroOV();
		calcularEnteroUF();
	}



	if(contadorcadena<tamanioCadena) {
		return false;
	}

	else
		return true;

}

int BitManager::getIntToDescompress(){

	this->finDescompresion= this->interpretCompresedString();

	return integer;
}

void BitManager::setFloor(unsigned int floor){

	this->floor = floor;
	this->stateControl->setPiso(floor);

}

void BitManager::setRoof(unsigned int roof){

	this->roof = roof;
	this->stateControl->setTecho(roof);

}






